package promotion

import (
	"fmt"
	"strings"
	"time"

	"mall/common/modelx"
	"mall/common/resultx"
	enum "mall/service/backend/common/enum/promotion"
	"mall/service/forum/api/internal/types"

	"github.com/shopspring/decimal"
	"github.com/sirupsen/logrus"
	"github.com/zeromicro/go-zero/core/logx"
)

// Context 上下文
type CouponContext struct {
	CouponInfo        *types.CouponInfo           ///
	UserId            uint64                      ///
	UserRank          string                      ///
	GemRatio          decimal.Decimal             ///
	MutexMap          map[uint32]map[uint64]int64 ///
	PricingInfos      []*PricingInfo              //
	OrderPricingInfos []*OrderPricingInfo
}

// Coupon 优惠券模板
type Coupon struct {
}

// checkParams 优惠券参数校验
func (coupon *Coupon) CheckParams(ctx *CouponContext) (err error) {
	logx.Infow("优惠券:参数校验...")

	return
}

// checkTimeRange 校验优惠券是否在可用时间段内
func (coupon *Coupon) CheckTimeRange(ctx *CouponContext) (err error) {
	// logx.Infow("校验优惠券是否在可用时间段内...")

	if len(ctx.CouponInfo.TimeRange) == 2 {
		now := time.Now()
		if startedAt, err := modelx.ParseLocalTime(ctx.CouponInfo.TimeRange[0]); err == nil {
			// logrus.Info(fmt.Sprintf("Coupon checkTimeRange startedAt: %+v   %+v   %+v", startedAt, startedAt.After(now), now))
			if startedAt.After(now) {
				return resultx.NewErrCode(resultx.COUPON_NOT_STARTED)
			}
		}
		if EndedAt, err := modelx.ParseLocalTime(ctx.CouponInfo.TimeRange[1]); err == nil {
			// logrus.Info(fmt.Sprintf("Coupon checkTimeRange EndedAt: %+v   %+v   %+v", EndedAt, EndedAt.Before(now), now))
			if EndedAt.Before(now) {
				return resultx.NewErrCode(resultx.COUPON_ENDED)
			}
		}
	}

	return
}

func (coupon *Coupon) CheckMemberRestriction(ctx *CouponContext) (err error) {
	if ctx.CouponInfo.MemberRestriction != nil && len(*ctx.CouponInfo.MemberRestriction) > 0 {
		if members := strings.Split(strings.ToLower(*ctx.CouponInfo.MemberRestriction), ","); len(members) > 0 {
			for _, member := range members {
				if ctx.UserRank == member {
					return
				}

				return resultx.NewErrCode(resultx.COUPON_MEMBER_RESTRICTED)
			}
		}
	}

	return
}

// getPricingInfo 获取定价信息
func (coupon *Coupon) GetPricingInfo(ctx *CouponContext) (err error) {
	// logx.Infow("优惠券:获取定价信息...")

	if len(ctx.PricingInfos) == 0 || len(ctx.OrderPricingInfos) == 0 {
		return
	}

	var checkMutex bool
	if ctx.CouponInfo.MutexType != nil && uint8(*ctx.CouponInfo.MutexType) == enum.PromotionMutexTypeSku {
		checkMutex = true
	}

	for _, info := range ctx.PricingInfos {
		if checkMutex {
			if subMap, ok := ctx.MutexMap[*ctx.CouponInfo.MutexType]; ok {
				if _, exist := subMap[info.SkuId]; exist {
					continue
				}
			}

			ctx.MutexMap[*ctx.CouponInfo.MutexType] = map[uint64]int64{info.SkuId: -1}
		}

		// todo:
	}

	return
}

func (coupon *Coupon) GetOrderPricingInfo(ctx *CouponContext) (err error) {
	if len(ctx.PricingInfos) == 0 || len(ctx.OrderPricingInfos) == 0 {
		return
	}

	if ctx.CouponInfo.CouponTemplateInfo.Threshold == nil {
		return
	}

	if f, err := decimal.NewFromString(*ctx.CouponInfo.CouponTemplateInfo.Threshold); err == nil {
		var mode uint8
		if ctx.CouponInfo.CouponTemplateInfo.Mode != nil {
			mode = uint8(*ctx.CouponInfo.CouponTemplateInfo.Mode)
		}

		fulfilled, threshold, margin := coupon.checkThreshold(ctx, f, mode)

		var couponId int64
		if ctx.CouponInfo.CouponId != nil {
			couponId = int64(*ctx.CouponInfo.CouponId)
		}
		ctx.OrderPricingInfos = append(ctx.OrderPricingInfos, &OrderPricingInfo{
			SkusNumber:       ctx.OrderPricingInfos[0].SkusNumber,
			SkusAmount:       ctx.OrderPricingInfos[0].SkusAmount,
			FinalAmount:      ctx.OrderPricingInfos[0].FinalAmount,
			CouponId:         couponId,
			CouponTemplateId: int64(*ctx.CouponInfo.CouponTemplateInfo.Id),
			Threshold:        threshold,
			Limit:            f,
			Status:           0,
		})
		var idx int = 1
		if fulfilled {
			return coupon.getOrderPricingDetailInfo(ctx, idx, mode)
		}

		ctx.OrderPricingInfos[idx].Status = -1
		ctx.OrderPricingInfos[idx].Margin = margin
	}

	return
}

func (coupon *Coupon) checkThreshold(
	ctx *CouponContext,
	threshhold decimal.Decimal,
	mode uint8,
) (bool, string, string) {
	var gemOrNum string
	switch mode {
	case enum.PromotionCouponModeAmount: // 满N元减送
		// gemOrNum = threshhold.Mul(ctx.GemRatio).Truncate(2).String()
		gemOrNum = threshhold.Truncate(2).String()
		if ctx.OrderPricingInfos[0].SkusAmount.GreaterThan(threshhold) {
			return true, gemOrNum, ""
		}

		// return false, gemOrNum, threshhold.Sub(ctx.OrderPricingInfos[0].SkusAmount).Mul(ctx.GemRatio).Truncate(2).String()
		return false, gemOrNum, threshhold.Sub(ctx.OrderPricingInfos[0].SkusAmount).Truncate(2).String()
	case enum.PromotionCouponModeNumber: // 满N件减送
		skusNumber := decimal.NewFromInt(ctx.OrderPricingInfos[0].SkusNumber)
		gemOrNum = threshhold.Truncate(0).String()
		if skusNumber.GreaterThanOrEqual(threshhold) {
			return true, gemOrNum, ""
		}

		return false, gemOrNum, threshhold.Sub(skusNumber).Truncate(0).String()
	}

	return false, gemOrNum, ""
}

func (coupon *Coupon) getOrderPricingDetailInfo(ctx *CouponContext, idx int, mode uint8) (err error) {
	if ctx.CouponInfo.CouponTemplateInfo.DiscountMode != nil && ctx.CouponInfo.CouponTemplateInfo.DiscountValue != nil {
		if f, err := decimal.NewFromString(*ctx.CouponInfo.CouponTemplateInfo.DiscountValue); err == nil {
			detail := ctx.OrderPricingInfos[idx]
			switch uint8(*ctx.CouponInfo.CouponTemplateInfo.DiscountMode) {
			case enum.PromotionCouponRuleAmount: // 满减
				if ctx.OrderPricingInfos[idx].FinalAmount.GreaterThan(f) {
					detail.CouponAmount = f
					detail.CouponDiscount = f.Div(ctx.OrderPricingInfos[idx].FinalAmount)
					detail.Status = 1
					ctx.OrderPricingInfos[idx] = detail
					ctx.OrderPricingInfos[idx].FinalAmount = ctx.OrderPricingInfos[idx].FinalAmount.Sub(f)
					err = coupon.distributePricingInfo(ctx, idx)
				} else {
					logrus.Info(fmt.Sprintf("Coupon getOrderPricingDetailInfo Discount GreaterThan Amount: %+v > %+v",
						f, ctx.OrderPricingInfos[idx].FinalAmount))
				}
			case enum.PromotionCouponRuleDiscount: // 满折
				var oneHundredDecimal decimal.Decimal = decimal.NewFromInt(100)
				if oneHundredDecimal.GreaterThan(f) {
					detail.CouponAmount =
						ctx.OrderPricingInfos[0].SkusAmount.Mul(oneHundredDecimal.Sub(f)).Div(oneHundredDecimal)
					detail.CouponDiscount = oneHundredDecimal.Sub(f)
					detail.FinalAmount = detail.FinalAmount.Sub(detail.CouponAmount)
					detail.Status = 1
					ctx.OrderPricingInfos[idx] = detail
					err = coupon.distributePricingInfo(ctx, idx)
				} else {
					logrus.Info(fmt.Sprintf("Coupon getOrderPricingDetailInfo Discount GreaterThan 100: %+v", f))
				}
			case enum.PromotionCouponRuleDeduction: // 抵扣（某Sku全部金额(一个数量)）
				//
			case enum.PromotionCouponRuleOrderFreeShipping: // 整单包邮
				detail.Effect = enum.PromotionEffectOrderFreeShipping
				detail.EffectType = enum.PromotionEffectTypeFreeShipping
				detail.Status = 0
				ctx.OrderPricingInfos[idx] = detail
			case enum.PromotionCouponRuleProductFreeShipping: // 仅活动商品包邮
				detail.Effect = enum.PromotionEffectProductFreeShipping
				detail.EffectType = enum.PromotionEffectTypeFreeShipping
				detail.Status = 0
				ctx.OrderPricingInfos[idx] = detail
			}
			if err != nil {
				return err
			}
		}
	}

	return
}

func (coupon *Coupon) distributePricingInfo(ctx *CouponContext, idx int) (err error) {
	for k, info := range ctx.PricingInfos {
		diff := info.FinalAmount.Mul(ctx.OrderPricingInfos[idx].CouponDiscount)
		info.CouponAmount = info.CouponAmount.Add(diff)
		info.FinalAmount = info.FinalAmount.Sub(diff)
		ctx.PricingInfos[k] = info
	}

	return
}
